package com.jmga.ecogasfinder.activities.maps.overlays;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.util.Log;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.Projection;
import com.jmga.ecogasfinder.utilities.Tools;
import com.jmga.ecogasfinder.vo.SerializableGeoPoint;

public class RouteOverlay extends Overlay {

	private GeoPoint locationA, locationB;
	private ArrayList<SerializableGeoPoint> geoRoutePoints;
	private Integer xMin, xMax, yMin, yMax;
	private String gmapsFeed;
	private double distance;

	public RouteOverlay(GeoPoint locationA, GeoPoint locationB, String gmapsFeed) {
		super();
		this.locationA = locationA;
		this.locationB = locationB;
		this.gmapsFeed = gmapsFeed;
		this.geoRoutePoints = getPoints(locationA, locationB);
		this.distance = Tools.getDistanceDriving(gmapsFeed, locationA, locationB);
	}

	@Override
	public void draw(Canvas canvas, MapView mapView, boolean shadow) {
		Projection projection = mapView.getProjection();

		if (!shadow && locationA != null && locationB != null) {
			Paint paint = new Paint();
			paint.setAntiAlias(true);
			paint.setStrokeWidth(1);
			paint.setAlpha(120);
			paint.setColor(Color.BLUE);
			
			Point pointA, pointB;
			
			SerializableGeoPoint last = null, actual = null;
			for(int i=0; this.geoRoutePoints != null && i < this.geoRoutePoints.size();i++) {
				actual = this.geoRoutePoints.get(i);
				if(last != null && last != actual) {
					pointA = new Point();
					pointB = new Point();
					
					projection.toPixels(last.getGeoPoint(), pointA);
					projection.toPixels(actual.getGeoPoint(), pointB);
					
					canvas.drawLine(pointA.x, pointA.y, pointB.x, pointB.y, paint);
				}
				last = actual;
			}
		}
		super.draw(canvas, mapView, shadow);
	}
	

	public ArrayList<SerializableGeoPoint> getGeoRoutePoints() {
		return this.geoRoutePoints;
	}

	private ArrayList<SerializableGeoPoint> getPoints(GeoPoint locationA, GeoPoint locationB) {
		ArrayList<SerializableGeoPoint> result = null;
		
		URL url;
		try {
			String origin, destination;

			origin = locationA.getLatitudeE6() / 1E6 + ","
					+ locationA.getLongitudeE6() / 1E6;
			destination = locationB.getLatitudeE6() / 1E6 + ","
					+ locationB.getLongitudeE6() / 1E6;
			url = new URL(gmapsFeed.concat("&origin=" + origin
					+ "&destination=" + destination));

			URLConnection connection;
			connection = url.openConnection();
			
			Log.d(getClass().getName(), "URL-:" + url.toExternalForm());

			HttpURLConnection httpConnection = (HttpURLConnection) connection;
			int responseCode = httpConnection.getResponseCode();

			if (responseCode == HttpURLConnection.HTTP_OK) {
				InputSource inputXml = new InputSource(httpConnection.getInputStream());
				XPathFactory factory = XPathFactory.newInstance();
				XPath xpath = factory.newXPath();

				NodeList nodes = (NodeList) xpath.evaluate(
						"/DirectionsResponse/route/overview_polyline/points",
						inputXml, XPathConstants.NODESET);
				String nodeString = nodes.item(0).getTextContent();
				result = decodePoly(nodeString);
				
				Log.d(getClass().getName(), result.size() + " points between these points");
			}
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (XPathExpressionException e) {
			e.printStackTrace();
		}
		
		return result;
	}

	private ArrayList<SerializableGeoPoint> decodePoly(String encoded) {
		ArrayList<SerializableGeoPoint> poly = new ArrayList<SerializableGeoPoint>();
		int index = 0, len = encoded.length();
		int lat = 0, lng = 0;

		while (index < len) {
			int b, shift = 0, result = 0;
			do {
				b = encoded.charAt(index++) - 63;
				result |= (b & 0x1f) << shift;
				shift += 5;
			} while (b >= 0x20);
			int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
			lat += dlat;

			shift = 0;
			result = 0;
			do {
				b = encoded.charAt(index++) - 63;
				result |= (b & 0x1f) << shift;
				shift += 5;
			} while (b >= 0x20);
			int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
			lng += dlng;

			SerializableGeoPoint p = new SerializableGeoPoint((int) (((double) lat / 1E5) * 1E6),
					(int) (((double) lng / 1E5) * 1E6));
			checkCoords(p.getLongitudeE6(), p.getLatitudeE6());
			poly.add(p);
		}

		return poly;
	}
	
	private void checkCoords(int longitude, int latitude) {
		xMin = Tools.checkCoord(xMin, longitude, true);
		xMax = Tools.checkCoord(xMax, longitude, false);
		yMin = Tools.checkCoord(yMin, latitude, true);
		yMax = Tools.checkCoord(yMax, latitude, false);
	}

	public int getxMin() {
		return xMin;
	}

	public int getxMax() {
		return xMax;
	}

	public int getyMin() {
		return yMin;
	}

	public int getyMax() {
		return yMax;
	}
	
	public double getDistance() {
		return this.distance;
	}
}
